#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <getopt.h>
#include <cliparse.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <i264e_common.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
typedef enum
{
	OPT_FRAMES = 256,
	OPT_THREAD_INPUT,
	OPT_QUIET,
	OPT_NOPROGRESS,
	OPT_INPUT,
	OPT_OUTPUT,
	OPT_SENSOR_TYPE,
	OPT_FPS,
	OPT_LOG_LEVEL,
	OPT_SOC,
	OPT_TLB,
	OPT_USEPHY,
	OPT_USERFENC,
	OPT_USERNALBUF,
	OPT_OPENCOOP,
	OPT_USEDN,
	OPT_DEVID,
	OPT_GOP,
	OPT_READCNT,
	OPT_PICCNT,
	OPT_REUSECNT,
} OptionsOPT;

static char short_options[] = "8f:hI:i:m:o:q:r:V";
static struct option long_options[] =
{
	{"soc",         required_argument, NULL, OPT_SOC},
	{"tlb",         required_argument, NULL, OPT_TLB},
	{"usephy",      required_argument, NULL, OPT_USEPHY},
	{"userfenc",    required_argument, NULL, OPT_USERFENC},
	{"usernalbuf",  required_argument, NULL, OPT_USERNALBUF},
	{"opencoop",    required_argument, NULL, OPT_OPENCOOP},
	{"usedn",		required_argument, NULL, OPT_USEDN},
	{"devid",       required_argument, NULL, OPT_DEVID},
	{"frames",      required_argument, NULL, OPT_FRAMES},
	{"gop",         required_argument, NULL, OPT_GOP},
	{"mref",        required_argument, NULL, 0},
	{"buf-share",   required_argument, NULL, 0},

	{"width",       required_argument, NULL, 0},
	{"height",      required_argument, NULL, 0},
	{"csp",         required_argument, NULL, 0},
	{"profile",     required_argument, NULL, 0},
	{"level-idc",   required_argument, NULL, 0},

	{"help",              no_argument, NULL, 'h'},
	{"version",           no_argument, NULL, 'V'},

	{"nf",                no_argument, NULL, 0},
	{"no-deblock",        no_argument, NULL, 0},
	{"filter",      required_argument, NULL, 0},
	{"deblock",     required_argument, NULL, 'f'},
	{"cabac",             no_argument, NULL, 0},
	{"no-cabac",          no_argument, NULL, 0},
	{"bitrate",     required_argument, NULL, 0},
	{"rc-method",   required_argument, NULL, 0},
	{"qp",          required_argument, NULL, 'q'},
	{"qpmin",       required_argument, NULL, 0},
	{"qpmax",       required_argument, NULL, 0},
	{"bitrate",     required_argument, NULL, 0},
	{"fps-min",     required_argument, NULL, 0},
	{"fps-max",     required_argument, NULL, 0},
	{"biaslvl",     required_argument, NULL, 0},
	{"qp-step",     required_argument, NULL, 0},
	{"gop-qp-step", required_argument, NULL, 0},
	{"adaptive_mode",required_argument, NULL, 0},
	{"gop-relation",required_argument, NULL, 0},
	{"static-time", required_argument, NULL, 0},
	{"max-bitrate", required_argument, NULL, 0},
	{"change-pos",  required_argument, NULL, 0},
	{"quality-level",required_argument, NULL, 0},
	{"trigger-level",required_argument, NULL, 0},
	{"new-qp-max-thr",required_argument, NULL, 0},
	{"enable-mbrc", required_argument, NULL, 0},
	{"enable-change-ref",required_argument, NULL, 0},
	{"qpstep",      required_argument, NULL, 0},
	{"ref",         required_argument, NULL, 'r'},
	{"sar",         required_argument, NULL, 0},
	{"fps",         required_argument, NULL, OPT_FPS},
	{"weightp",     required_argument, NULL, 0},
	{"mvrange",     required_argument, NULL, 0},
	{"subme",       required_argument, NULL, 'm'},
	{"8x8dct",            no_argument, NULL, '8'},
	{"no-8x8dct",         no_argument, NULL, 0},
	{"level",       required_argument, NULL, 0},
	{"ratetol",     required_argument, NULL, 0},
	{"ipratio",     required_argument, NULL, 0},
	{"pbratio",     required_argument, NULL, 0},
	{"chroma-qp-offset", required_argument, NULL, 0},
	{"threads",     required_argument, NULL, 0},
	{"slices",            required_argument, NULL, 0},
	{"thread-input",      no_argument, NULL, OPT_THREAD_INPUT},
	{"psnr",              no_argument, NULL, 0},
	{"ssim",              no_argument, NULL, 0},
	{"log-level",   required_argument, NULL, OPT_LOG_LEVEL},
	{"no-progress",       no_argument, NULL, OPT_NOPROGRESS},
	{"output",      required_argument, NULL, OPT_OUTPUT},
	{"input",       required_argument, NULL, OPT_INPUT},
	{"sensor-type", required_argument, NULL, OPT_SENSOR_TYPE},
	{"sps-id",      required_argument, NULL, 0},
	{"overscan",    required_argument, NULL, 0},
	{"videoformat", required_argument, NULL, 0},
	{"colorprim",   required_argument, NULL, 0},
	{"transfer",    required_argument, NULL, 0},
	{"colormatrix", required_argument, NULL, 0},
	{"chromaloc",   required_argument, NULL, 0},
	{"force-cfr",         no_argument, NULL, 0},
	{"dntype",		required_argument, NULL, 0},
	{"idnqp",		required_argument, NULL, 0},
	{"pdnqp",		required_argument, NULL, 0},
	{"bssave",		required_argument, NULL, 0},
	{"refsave",		required_argument, NULL, 0},
	{"encsave",		required_argument, NULL, 0},
	{"decsave",		required_argument, NULL, 0},
	{"readcnt",     required_argument, NULL, OPT_READCNT},
	{"piccnt",      required_argument, NULL, OPT_PICCNT},
	{"reusecnt",    required_argument, NULL, OPT_REUSECNT},
	{0, 0, 0, 0}
};

static int i264e_atobool(const char *str, int *b_error)
{
	if( !strcmp(str, "1") ||
		!strcmp(str, "true") ||
		!strcmp(str, "yes") )
		return 1;
	if( !strcmp(str, "0") ||
		!strcmp(str, "false") ||
		!strcmp(str, "no") )
		return 0;
	*b_error = 1;
	return 0;
}

static int i264e_atoi(const char *str, int *b_error)
{
	char *end;
	int v = strtol(str, &end, 0);
	if(end == str || *end != '\0')
		*b_error = 1;
	return v;
}

static double i264e_atof(const char *str, int *b_error)
{
	char *end;
	double v = strtod(str, &end);
	if(end == str || *end != '\0')
		*b_error = 1;
	return v;
}
static int parse_enum( const char *arg, const char * const *names, int *dst )
{
	int i = 0;
	for(i = 0; names[i]; i++ )
		if( !strcmp( arg, names[i] ) )
		{
			*dst = i;
			return 0;
		}
	return -1;
}

#define atobool(str) (name_was_bool = 1, i264e_atobool(str, &b_error))
#undef atoi
#undef atof
#define atoi(str) i264e_atoi(str, &b_error)
#define atof(str) i264e_atof(str, &b_error)

int i264e_param_parse(i264e_param_t *p, const char *name, const char *value)
{
	char *name_buf = NULL;
	int b_error = 0;
	int name_was_bool;
	int value_was_null = !value;
	int i;

	if(!name)
		return -1;
	if(!value)
		value = "true";

	if(value[0] == '=')
		value++;

	if(strchr( name, '_' )) // s/_/-/g
	{
		char *c;
		name_buf = strdup(name);
		while( (c = strchr(name_buf, '_' )))
			*c = '-';
		name = name_buf;
	}

	if( (!strncmp(name, "no-", 3 ) && (i = 3)) ||
		(!strncmp(name, "no", 2 ) && (i = 2)))
	{
		name += i;
		value = atobool(value) ? "false" : "true";
	}
	name_was_bool = 0;

#define OPT(STR) else if(!strcmp( name, STR ))
#define OPT2(STR0, STR1) else if(!strcmp(name, STR0) || !strcmp(name, STR1))
	if(0);
	OPT("soc")
	{
		if (!strcmp("m200", value))
			p->soc = C_M200;
		else if (!strcmp("t10", value))
			p->soc = C_T10;
		else if (!strcmp("t20", value))
			p->soc = C_T20;
        else if (!strcmp("t30", value))
			p->soc = C_T30;
		else if (!strcmp("t21", value))
			p->soc = C_T21;
		else if (!strcmp("t23", value))
			p->soc = C_T23;
		else
			b_error = 1;
	}
	OPT("tlb")
	{
		p->b_tlb = atobool(value);
	}
	OPT("usephy")
	{
		p->b_use_phy = atobool(value);
	}
	OPT("userfenc")
	{
		p->b_user_fenc = atobool(value);
	}
	OPT("usernalbuf")
	{
		p->b_user_nalbuffer = atobool(value);
	}
	OPT("opencoop")
	{
		p->b_open_coop = atobool(value);
	}
	OPT("usedn")
	{
		p->b_use_dn = atobool(value);
	}
	OPT("devid")
	{
		p->i_devid = atoi(value);
	}
	OPT("threads")
	{
		if( !strcmp(value, "auto") )
			p->i_threads = 0;
		else
			p->i_threads = atoi(value);
	}
	OPT("gop")
	{
		p->i_gop = atoi(value);
	}
	OPT("mref")
	{
		p->b_mref = atobool(value);
	}
	OPT("buf-share")
	{
		p->b_buf_share = atobool(value);
	}
	OPT("width")
	{
		p->i_width = atoi(value);
	}
	OPT("height")
	{
		p->i_height = atoi(value);
	}
	OPT("csp")
	{
		p->i_csp = atoi(value);
	}
	OPT("level-idc")
	{
		p->i_level_idc = atoi(value);
	}
	OPT2("level", "level-idc")
	{
		if(!strcmp(value, "1b"))
			p->i_level_idc = 9;
		else if(atof(value) < 6)
			p->i_level_idc = (int)(10*atof(value)+.5);
		else
			p->i_level_idc = atoi(value);
	}
	OPT("profile")
	{
		if (!strcmp(value, "baseline"))
			p->i_profile = PROFILE_BASELINE;
		else if (!strcmp(value, "main"))
			p->i_profile = PROFILE_MAIN;
		else if (!strcmp(value, "high"))
			p->i_profile = PROFILE_HIGH;
	}
	OPT("sar")
	{
		b_error = ( 2 != sscanf( value, "%d:%d", &p->vui.i_sar_width, &p->vui.i_sar_height ) &&
					2 != sscanf( value, "%d/%d", &p->vui.i_sar_width, &p->vui.i_sar_height ) );
	}
	OPT("overscan")
		b_error |= parse_enum( value, i264e_overscan_names, &p->vui.i_overscan );
	OPT("videoformat")
		b_error |= parse_enum( value, i264e_vidformat_names, &p->vui.i_vidformat );
	OPT("fullrange")
		b_error |= parse_enum( value, i264e_fullrange_names, &p->vui.b_fullrange );
	OPT("colorprim")
		b_error |= parse_enum( value, i264e_colorprim_names, &p->vui.i_colorprim );
	OPT("transfer")
		b_error |= parse_enum( value, i264e_transfer_names, &p->vui.i_transfer );
	OPT("colormatrix")
		b_error |= parse_enum( value, i264e_colmatrix_names, &p->vui.i_colmatrix );
	OPT("chromaloc")
	{
		p->vui.i_chroma_loc = atoi(value);
		b_error = (p->vui.i_chroma_loc < 0 || p->vui.i_chroma_loc > 5);
	}
	OPT("fps")
	{
		if(sscanf( value, "%u/%u", &p->i_fps_num, &p->i_fps_den ) == 2)
			;
		else
		{
			float fps = atof(value);
			if( fps > 0 && fps <= INT_MAX/1000 )
			{
				p->i_fps_num = (int)(fps * 1000 + .5);
				p->i_fps_den = 1000;
			}
			else
			{
				p->i_fps_num = atoi(value);
				p->i_fps_den = 1;
			}
		}
	}
	OPT2("ref", "frameref")
		p->i_frame_reference = atoi(value);
	OPT("dpb-size")
		p->i_dpb_size = atoi(value);
	OPT("nf")
		p->b_deblocking_filter = !atobool(value);
	OPT2("filter", "deblock")
	{
		if( 2 == sscanf( value, "%d:%d", &p->i_deblocking_filter_alphac0, &p->i_deblocking_filter_beta ) ||
			2 == sscanf( value, "%d,%d", &p->i_deblocking_filter_alphac0, &p->i_deblocking_filter_beta ) )
		{
			p->b_deblocking_filter = 1;
		}
		else if( sscanf( value, "%d", &p->i_deblocking_filter_alphac0 ) )
		{
			p->b_deblocking_filter = 1;
			p->i_deblocking_filter_beta = p->i_deblocking_filter_alphac0;
		}
		else
			p->b_deblocking_filter = atobool(value);
	}
	OPT("slices")
		p->i_slice_count = atoi(value);
	OPT("cabac")
		p->b_cabac = atobool(value);
	OPT("cabac-idc")
		p->i_cabac_init_idc = atoi(value);
	OPT("log")
		p->i_log_level = atoi(value);
	OPT("8x8dct")
		p->analyse.b_transform_8x8 = atobool(value);
	OPT("weightp")
		p->analyse.i_weighted_pred = atoi(value);
	OPT("chroma-qp-offset")
		p->analyse.i_chroma_qp_offset = atoi(value);
	OPT2("mvrange", "mv-range")
		p->analyse.i_mv_range = atoi(value);
	OPT2("subme", "subq")
		p->analyse.i_subpel_refine = atoi(value);
	OPT("rc-method")
	{
		if(!strcmp(value, "cqp"))
			p->rc.i_rc_method = I264E_RC_CQP;
		else if(!strcmp(value, "cbr"))
			p->rc.i_rc_method = I264E_RC_CBR;
		else if(!strcmp(value, "vbr"))
			p->rc.i_rc_method = I264E_RC_VBR;
		else if(!strcmp(value, "smart"))
			p->rc.i_rc_method = I264E_RC_SMART;
		else
			fprintf(stderr, "rc-method param err :%s\n", value);
	}
	OPT("bitrate")
	{
		p->rc.i_bitrate = atoi(value);
	}
	OPT2("qp", "qp_constant")
	{
		p->rc.i_qp_constant = atoi(value);
		p->rc.i_rc_method = I264E_RC_CQP;
	}
	OPT2("qpmin", "qp-min")
		p->rc.i_qp_min = atoi(value);
	OPT2("qpmax", "qp-max")
		p->rc.i_qp_max = atoi(value);
	OPT2("qpstep", "qp-step")
		p->rc.i_qp_step = atoi(value);
	OPT("fps-min")
		p->rc.i_fps_min = atoi(value);
	OPT("fps-max")
		p->rc.i_fps_max = atoi(value);
	OPT("biaslvl")
		p->rc.i_biaslvl = atoi(value);
	OPT("qp-step")
		p->rc.i_qp_step = atoi(value);
	OPT("gop-qp-step")
		p->rc.i_gop_qp_step = atoi(value);
	OPT("adaptive-mode")
		p->rc.b_adaptive_mode = atobool(value);
	OPT("gop-relation")
		p->rc.b_gop_relation = atobool(value);
	OPT("static-time")
		p->rc.i_static_time = atoi(value);
	OPT("max-bitrate")
		p->rc.i_max_bitrate = atoi(value);
	OPT("change-pos")
		p->rc.i_change_pos = atoi(value);
	OPT("quality-level")
		p->rc.i_quality_level = atoi(value);
	OPT("trigger-level")
		p->rc.i_trigger_level = atoi(value);
	OPT("new-qp-max-thr")
		p->rc.i_new_qp_max_thr = atoi(value);
	OPT("enable-mbrc")
		p->rc.b_enable_mbrc = atobool(value);
	OPT("enable-change-ref")
		p->rc.b_enable_change_ref = atobool(value);
	OPT("ratetol")
		p->rc.f_rate_tolerance = !strncmp("inf", value, 3) ? 1e9 : atof(value);
	OPT2("ipratio", "ip-factor")
		p->rc.f_ip_factor = atof(value);
	OPT2("pbratio", "pb-factor")
		p->rc.f_pb_factor = atof(value);
	OPT("psnr")
		p->analyse.b_psnr = atobool(value);
	OPT("ssim")
		p->analyse.b_ssim = atobool(value);
	OPT("sps-id")
		p->i_sps_id = atoi(value);
	OPT("global-header")
		p->b_repeat_headers = !atobool(value);
	OPT("repeat-headers")
		p->b_repeat_headers = atobool(value);
	OPT("annexb")
		p->b_annexb = atobool(value);
	OPT("force-cfr")
		p->b_vfr_input = !atobool(value);
	OPT("dntype")
		p->denoise.i_dn_type = atoi(value);
	OPT("idnqp")
		p->denoise.i_itype_qp = atoi(value);
	OPT("pdnqp")
		p->denoise.i_ptype_qp = atoi(value);
	OPT("bssave")
	{
		p->b_bs_save = open(value, O_RDWR | O_CREAT | O_TRUNC, 0777);
		if (p->b_bs_save < 0) {
			printf("bssave: open %s failed:%s\n", value, strerror(errno));
		}
	}
	OPT("refsave")
	{
		p->b_ref_save = open(value, O_RDWR | O_CREAT | O_TRUNC, 0777);
		if (p->b_ref_save < 0) {
			printf("refsave: open %s failed:%s\n", value, strerror(errno));
		}
	}
	OPT("encsave")
	{
		p->b_enc_save = open(value, O_RDWR | O_CREAT | O_TRUNC, 0777);
		if (p->b_enc_save < 0) {
			printf("encsave: open %s failed:%s\n", value, strerror(errno));
		}
	}
	OPT("decsave")
	{
		p->b_dec_save = open(value, O_RDWR | O_CREAT | O_TRUNC, 0777);
		if (p->b_dec_save < 0) {
			printf("decsave: open %s failed:%s\n", value, strerror(errno));
		}
	}
	else
		return -1;
#undef OPT
#undef OPT2
#undef atobool
#undef atoi
#undef atof
	if(name_buf)
		free(name_buf);

	b_error |= value_was_null && !name_was_bool;
	return b_error ? -1 : 0;
}

static const char * const log_level_names[] = {"none", "error", "warning", "info", "debug", 0};

static char const *strtable_lookup( const char * const table[], int idx )
{
	int i = 0; while( table[i] ) i++;
	return ( ( idx >= 0 && idx < i ) ? table[ idx ] : "???" );
}

static char *stringify_names( char *buf, const char * const names[] )
{
	int i = 0;
	char *p = buf;
	for( p[0] = 0; names[i]; i++ )
	{
		p += sprintf( p, "%s", names[i] );
		if( names[i+1] )
			p += sprintf( p, ", " );
	}
	return buf;
}
static void help(i264e_param_t *defaults)
{
	char buf[50];
#define H0 printf
	H0("Syntax: i264e [options] -o outfile infile\n"
	  );
	H0( "\n" );
	H0( "Frame-type options:\n" );
	H0( "\n" );
	H0( "      --soc m200/t10/t20		 [m200]\n");
	H0( "      --tlb <integer>		 [%d]\n", defaults->b_tlb);
	H0( "      --usephy <integer>	 [%d]\n", defaults->b_use_phy);
	H0( "      --userfenc <integer>  [%d]\n", defaults->b_user_fenc);
	H0( "      --usernalbuf <integer> [%d]\n", defaults->b_user_nalbuffer);
	H0( "      --opencoop <integer>  [%d]\n", defaults->b_open_coop);
	H0( "      --usedn <integer>	 [%d]\n", defaults->b_use_dn);
	H0( "      --devid <integer>	 [%d]\n", defaults->i_devid);
	H0( "      --gop <integer>       GOP size [%d]\n", defaults->i_gop);
	H0( "      --width <integer>	 Video width\n");
	H0( "      --height <integer>	 Video width\n");
	H0( "      --csp <integer>		 encoder color space [%d], 1:nv12, 2:nv21, 3:t420 only these three type support\n", defaults->i_csp);

	H0( "      --profile <string>	 h264 encode profile, support baseline, main, high\n");
	H0( "      --level-idc <integer> [%d]\n", defaults->i_level_idc);
	H0( "      --no-cabac              Disable CABAC\n" );
	H0( "      --no-deblock            Disable loop filter\n" );
	H0( "      --dntype	<integer>	   0: unuse dn, 1: usedn all i, 2: usedn ip\n" );
	H0( "      --idnqp <integer>	   i frame denoise qp\n" );
	H0( "      --pdnqp <integer>	   p frame denoise qp\n" );
	H0( "\n" );
	H0( "Ratecontrol:\n" );
	H0( "\n" );
	H0( "      --rc-method <string>    cqp/cbr/vbr/abr\n");
	H0( "  -q, --qp <integer>          Force constant QP (0-51, 0=lossless)\n");
	H0( "  -B, --bitrate <integer>     Set bitrate (kbit/s)\n" );
	H0( "      --qpmin <integer>       Set min QP [%d]\n", defaults->rc.i_qp_min );
	H0( "      --qpmax <integer>       Set max QP [%d]\n", defaults->rc.i_qp_max );
	H0( "      --qpstep <integer>      Set max QP step [%d]\n", defaults->rc.i_qp_step );
	H0( "      --ratetol <float>       Tolerance of ABR ratecontrol and VBV [%.1f]\n", defaults->rc.f_rate_tolerance );
	H0( "      --ipratio <float>       QP factor between I and P [%.2f]\n", defaults->rc.f_ip_factor );
	H0( "      --pbratio <float>       QP factor between P and B [%.2f]\n", defaults->rc.f_pb_factor );
	H0( "      --chroma-qp-offset <integer>  QP difference between chroma and luma [%d]\n", defaults->analyse.i_chroma_qp_offset );
	H0( "\n" );
	H0( "Analysis:\n" );
	H0( "\n" );
	H0( "      --weightp <integer>     Weighted prediction for P-frames [%d]\n"
	    "                                  - 0: Disabled\n"
	    "                                  - 1: Weighted refs\n"
	    "                                  - 2: Weighted refs + Duplicates\n", defaults->analyse.i_weighted_pred );
	H0( "      --mvrange <integer>     Maximum motion vector length [-1 (auto)]\n" );
	H0( "  -m, --subme <integer>       Subpixel motion estimation and mode decision [%d]\n", defaults->analyse.i_subpel_refine );
	H0( "                                  - 0: fullpel only (not recommended)\n"
	    "                                  - 1: SAD mode decision, one qpel iteration\n"
	    "                                  - 2: SATD mode decision\n"
	    "                                  - 3-5: Progressively more qpel\n"
	    "                                  - 6: RD mode decision for I/P-frames\n"
	    "                                  - 7: RD mode decision for all frames\n"
	    "                                  - 8: RD refinement for I/P-frames\n"
	    "                                  - 9: RD refinement for all frames\n"
	    "                                  - 10: QP-RD - requires trellis=2, aq-mode>0\n"
	    "                                  - 11: Full RD: disable all early terminations\n" );
	H0( "      --no-8x8dct             Disable adaptive spatial transform size\n" );
	H0( "\n" );
	H0( "Video Usability Info (Annex E):\n" );
	H0( "The VUI settings are not used by the encoder but are merely suggestions to\n" );
	H0( "the playback equipment. See doc/vui.txt for details. Use at your own risk.\n" );
	H0( "\n" );
	H0( "      --overscan <string>     Specify crop overscan setting [\"%s\"]\n"
	    "                                  - undef, show, crop\n",
	                                   strtable_lookup( i264e_overscan_names, defaults->vui.i_overscan ) );
	H0( "      --videoformat <string>  Specify video format [\"%s\"]\n"
	    "                                  - component, pal, ntsc, secam, mac, undef\n",
	                                   strtable_lookup( i264e_vidformat_names, defaults->vui.i_vidformat ) );
	H0( "      --colorprim <string>    Specify color primaries [\"%s\"]\n"
	    "                                  - undef, bt709, bt470m, bt470bg, smpte170m,\n"
	    "                                    smpte240m, film, bt2020\n",
	                                   strtable_lookup( i264e_colorprim_names, defaults->vui.i_colorprim ) );
	H0( "      --transfer <string>     Specify transfer characteristics [\"%s\"]\n"
	    "                                  - undef, bt709, bt470m, bt470bg, smpte170m,\n"
	    "                                    smpte240m, linear, log100, log316,\n"
	    "                                    iec61966-2-4, bt1361e, iec61966-2-1,\n"
	    "                                    bt2020-10, bt2020-12\n",
	                                   strtable_lookup( i264e_transfer_names, defaults->vui.i_transfer ) );
	H0( "      --colormatrix <string>  Specify color matrix setting [\"%s\"]\n"
	    "                                  - undef, bt709, fcc, bt470bg, smpte170m,\n"
	    "                                    smpte240m, GBR, YCgCo, bt2020nc, bt2020c\n",
	                                   strtable_lookup( i264e_colmatrix_names, defaults->vui.i_colmatrix ) );
	H0( "      --chromaloc <integer>   Specify chroma sample location (0 to 5) [%d]\n",
	                                   defaults->vui.i_chroma_loc);
	H0( "\n" );
	H0( "Input/Output:\n" );
	H0( "\n" );
	H0( "      --input <string>		   Input method,raw:input frame normal yuv file,v4l2:input from sensor under v4l2 platform\n");
	H0( "      --sensor-type           v4l2 sensor type: support:ov9712,ov9732,ov9750,ar0141,gc1004,jxh42,sc1035\n");
	H0( "  -o, --output <string>       Specify output file\n");
	H0( "      --fps <float|rational>  Specify framerate\n");
	H0( "      --no-progress           Don't show the progress indicator while encoding\n");
	H0( "      --quiet                 Quiet Mode\n");
	H0( "      --log-level <string>    Specify the maximum level of logging [\"%s\"]\n"
	    "                                  - %s\n", strtable_lookup(log_level_names, defaults->i_log_level - C_LOG_NONE),
	                                   stringify_names(buf, log_level_names));
	H0( "      --psnr                  Enable PSNR computation\n");
	H0( "      --ssim                  Enable SSIM computation\n");
	H0( "      --threads <integer>     Force a specific number of threads\n");
	H0( "Other param \n");
	H0( "	   --readcnt			   only encode the first readcnt frame source yuv buf, default -1 or 0\n");
	H0( "	   --piccnt				   encode loop source yuv buf count, defaut 1 \n");
	H0( "	   --reusecnt			   source yuv buf reused encode count, defaut 1 \n");
	H0( "	   --bssave				   Save hardware(vpu) bs, default false \n");
	H0( "	   --refsave				Save hardware(vpu) bs, default false \n");
	H0( "	   --encsave			   Save hardware(vpu) bs, default false \n");
	H0( "	   --decsave			   Save hardware(vpu) bs, default false \n");

	H0( "\n" );
}

static int parse_enum_value( const char *arg, const char * const *names, int *dst )
{
	int i = 0;
	for(; names[i]; i++ )
		if( !strcasecmp( arg, names[i] ) )
		{
			*dst = i;
			return 0;
		}
	return -1;
}

int parse(int argc, char **argv, i264e_param_t *param, cli_opt_t *opt)
{
	if (argc == 1) {
		i264e_param_t d;
		memset(&d, 0, sizeof(i264e_param_t));
		i264e_param_default(&d);
		help(&d);
		exit(0);
	}
	/*The global variable must be initialized.*/
	optarg = NULL, optind = 1, opterr = 1, optopt = 0;
	/* Parse command line options */
	for( optind = 0;; )
	{
		int b_error = 0;
		int long_options_index = -1;

		int c = getopt_long(argc, argv, short_options, long_options, &long_options_index);

		if( c == -1 )
		{
			break;
		}
		switch( c )
		{
			case 'h':
				{
					i264e_param_t d;
					memset(&d, 0, sizeof(i264e_param_t));
					i264e_param_default(&d);
					help(&d);
					exit(0);
				}
			case OPT_FRAMES:
				opt->totalframe = C_MAX(atoi(optarg), 0);
				break;
			case OPT_OUTPUT:
			case 'o':
				opt->output_file_name = optarg;
				break;
			case OPT_THREAD_INPUT:
				opt->b_thread_input = 1;
				break;
			case OPT_QUIET:
				param->i_log_level = C_LOG_NONE;
				break;
			case 'v':
				param->i_log_level = C_LOG_DEBUG;
				break;
			case OPT_LOG_LEVEL:
				if(!parse_enum_value(optarg, &log_level_names[1], &(param->i_log_level)))
					param->i_log_level += C_LOG_NONE;
				else
					param->i_log_level = atoi( optarg );
				break;
			case OPT_NOPROGRESS:
				opt->b_progress = 0;
				break;
			case OPT_INPUT:
				if (!strcmp(optarg, "raw"))
					opt->input = 0;
				else if (!strcmp(optarg, "v4l2"))
					opt->input = 1;
				else
					b_error = 1;
				break;
			case OPT_SENSOR_TYPE:
				if (!strcmp(optarg, "ov9712")) {
					opt->sensor_type = SENSOR_OV9712;
				} else if (!strcmp(optarg, "ov9732")) {
					opt->sensor_type = SENSOR_OV9732;
				} else if (!strcmp(optarg, "ov9750")) {
					opt->sensor_type = SENSOR_OV9750;
				} else if (!strcmp(optarg, "ar0141")) {
					opt->sensor_type = SENSOR_AR0141;
				} else if (!strcmp(optarg, "gc1004")) {
					opt->sensor_type = SENSOR_GC1004;
				} else if (!strcmp(optarg, "jxh42")) {
					opt->sensor_type = SENSOR_JXH42;
				} else if (!strcmp(optarg, "sc1035")) {
					opt->sensor_type = SENSOR_SC1035;
				} else {
					opt->sensor_type = SENSOR_OV9712;
				}
			case OPT_READCNT:
				opt->read_cnt = C_MAX(atoi(optarg), 0);
				break;
			case OPT_PICCNT:
				opt->pic_cnt = C_MAX(atoi(optarg), 1);
				break;
			case OPT_REUSECNT:
				opt->reuse_cnt = C_MAX(atoi(optarg), 1);
				break;
			default:
			{
				if(long_options_index < 0)
				{
					int i = 0;
					for(; long_options[i].name; i++)
						if(long_options[i].val == c)
						{
							long_options_index = i;
							break;
						}
					if(long_options_index < 0)
					{
						/* getopt_long already printed an error message */
						return -1;
					}
				}
				b_error |= i264e_param_parse( param, long_options[long_options_index].name, optarg );
			}
		}
		if( b_error )
		{
			const char *name = long_options_index > 0 ? long_options[long_options_index].name : argv[optind-2];
			printf("invalid argument: %s = %s\n", name, optarg );
			return -1;
		}
	}
	opt->input_file_name = argv[optind++];
	/*get width and height from file name*/
	{
		char *p = NULL;
		for(p = opt->input_file_name; *p; p++ )
			if(*p >= '0' && *p <= '9' && sscanf( p, "%dx%d", &param->i_width, &param->i_height ) == 2)
				break;
	}
	opt->image_width=param->i_width;
	opt->image_height = param->i_height;
	return 0;
}

int deparse(const i264e_param_t *param, const cli_opt_t *opt)
{
	if (param->b_bs_save > 0) {
		close(param->b_bs_save);
	}
	if (param->b_ref_save > 0) {
		close(param->b_ref_save);
	}
	if (param->b_enc_save > 0) {
		close(param->b_enc_save);
	}
	if (param->b_dec_save > 0) {
		close(param->b_dec_save);
	}

	return 0;
}
